﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Text;
using MySql.Data.MySqlClient;

namespace MySqlManager
{
    /// <summary>
    /// Layer class over a DataTable used to read data from a MySQL database table
    /// </summary>
    public class MySqlReader : SqlQuery, IDisposable
    {
        /// <summary>
        /// List of columns that will be selected from the database table
        /// </summary>
        private List<string> columnsNeeded;
        
        /// <summary>
        /// Gets or sets the DataTable containing all the rows read from the database table
        /// </summary>
        private DataTable dataTable;

        /// <summary>
        /// Gets or sets the Row being read from dataTable
        /// </summary>
        private DataRow currentRow;

        /// <summary>
        /// Gets or sets the the index of the current Row in the DataTable 
        /// </summary>
        private int currentRowPosition;

        /// <summary>
        /// Initializes a new instance of the <see cref="MySqlReader"/> class
        /// </summary>
        public MySqlReader()
            : base()
        {
            this.columnsNeeded = new List<string>();
            this.currentRowPosition = 0;
            this.executeDelegate = this.ExecuteReader;
            this.dataTable = new DataTable();
        }

        /// <summary>
        /// Selects a column that will be read from the database table
        /// </summary>
        /// <param name="column">Name of the column</param>
        public void Select(string column)
        {
            this.columnsNeeded.Add(column);
        }
       
        /// <summary>
        /// Check if exists a next Row in the DataTable that can be read
        /// </summary>
        /// <returns>returns true if the DataTable contains a next Row</returns>
        public bool Read()
        {
            if (this.dataTable.Rows.Count > this.currentRowPosition)
            {
                this.currentRow = this.dataTable.Rows[this.currentRowPosition];
                this.currentRowPosition++;
                return true;
            }

            return false;
        }
        
        /// <summary>
        /// Read a String from the DataTable
        /// </summary>
        /// <param name="column">Name of the column from the Row containing the String</param>
        /// <returns>Returns the String associated to the the column from the current Row</returns>
        public string ReadString(string column)
        {
            return this.currentRow[column].ToString();
        }

        /// <summary>
        /// Read an SByte from the DataTable
        /// </summary>
        /// <param name="column">Name of the column from the Row containing the SByte</param>
        /// <returns>Returns the SByte associated to the the column from the current Row</returns>
        public sbyte ReadSByte(string column)
        {
            return new CastableString(this.currentRow[column]);
        }

        /// <summary>
        /// Read an Byte from the DataTable
        /// </summary>
        /// <param name="column">Name of the column from the Row containing the Byte</param>
        /// <returns>Returns the Byte associated to the the column from the current Row</returns>
        public byte ReadByte(string column)
        {
            return new CastableString(this.currentRow[column]);
        }

        /// <summary>
        /// Read an Short from the DataTable
        /// </summary>
        /// <param name="column">Name of the column from the Row containing the Short</param>
        /// <returns>Returns the Short associated to the the column from the current Row</returns>
        public short ReadInt16(string column)
        {
            return new CastableString(this.currentRow[column]);
        }

        /// <summary>
        /// Read an UShort from the DataTable
        /// </summary>
        /// <param name="column">Name of the column from the Row containing the UShort</param>
        /// <returns>Returns the UShort associated to the the column from the current Row</returns>
        public ushort ReadUInt16(string column)
        {
            return new CastableString(this.currentRow[column]);
        }

        /// <summary>
        /// Read an Integer from the DataTable
        /// </summary>
        /// <param name="column">Name of the column from the Row containing the Int</param>
        /// <returns>Returns the Int associated to the the column from the current Row</returns>
        public int ReadInt32(string column)
        {
            return new CastableString(this.currentRow[column]);
        }

        /// <summary>
        /// Read an UInt from the DataTable
        /// </summary>
        /// <param name="column">Name of the column from the Row containing the UInt</param>
        /// <returns>Returns the UInt associated to the the column from the current Row</returns>
        public uint ReadUInt32(string column)
        {
            return new CastableString(this.currentRow[column]);
        }

        /// <summary>
        /// Read an Long from the DataTable
        /// </summary>
        /// <param name="column">Name of the column from the Row containing the Long</param>
        /// <returns>Returns the Long associated to the the column from the current Row</returns>
        public long ReadInt64(string column)
        {
            return new CastableString(this.currentRow[column]);
        }

        /// <summary>
        /// Read an ULong from the DataTable
        /// </summary>
        /// <param name="column">Name of the column from the Row containing the ULong</param>
        /// <returns>Returns the ULong associated to the the column from the current Row</returns>
        public ulong ReadUInt64(string column)
        {
            return new CastableString(this.currentRow[column]);
        }

        /// <summary>
        /// Read a Byte[] from the DataTable
        /// </summary>
        /// <param name="column">Name of the column from the Row containing the Byte[]</param>
        /// <returns>Returns the Byte[] associated to the the column from the current Row</returns>
        public byte[] ReadByteArray(string column)
        {
            return new CastableString(this.currentRow[column]);
        }

        /// <summary>
        /// Release all resources used by the MySqlInsert
        /// </summary>
        public void Dispose()
        {
            this.Dispose(true);
        }

        /// <summary>
        /// Release the resources used by the MySqlInsert
        /// </summary>
        /// <param name="disposeNative">true if native objects should be disposed</param>
        private void Dispose(bool disposeNative)
        {
            if (disposeNative)
            {
                //dispose any native objects here
            }
            this.dataTable.Dispose();
            this.columnsNeeded.Clear();
            this.mySqlCommand.Dispose();
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Method called by the SQLQuery base methods Execute and BeginExecute
        /// </summary>
        private void ExecuteReader()
        {
            StringBuilder cmd = new StringBuilder("Select ");
            cmd.Append((this.columnsNeeded.Count > 0) ? string.Join(",", this.columnsNeeded) : "*")
                .Append(" From ")
                .Append(this.tableName);
            this.AppendWhereStatments(cmd);
            if (this.limit != null)
            {
                cmd.Append(" Limit=")
                    .Append(this.limit);
            }

            this.mySqlCommand.CommandText = cmd.ToString();
            using (this.mySqlCommand.Connection = MySqlCore.GetConnection())
            {
                using (MySqlDataAdapter adapter = new MySqlDataAdapter(this.mySqlCommand))
                {
                    adapter.Fill(this.dataTable);
                }
            }
        }
    }
}
